package com.twu.biblioteca;

import java.lang.reflect.*;
import java.util.*;


interface BibliotecaCommand {
    Map runCommand();
}


public class Biblioteca {
    private ArrayList<Book> availableBooks = new ArrayList<Book>();
    private final String[] availableMenuOptions  = new String[] {"List Books", "Return Book"};
    private ArrayList<Book> rentedBooks = new ArrayList<Book>();
    private final Map<String, BibliotecaCommand> methodMap = new HashMap<String, BibliotecaCommand>();
    private final Map<String, Map> submenuMasterMap = new HashMap<String, Map>();
    private final Map<String, BibliotecaCommand> submenuMethodMap = new HashMap<String, BibliotecaCommand>();

//INSTANCE SETUP METHODS
    public Biblioteca() {
        String[] availableBooksTitles  = new String[] {"Category wise tutorials - J2EE", "JAAS in Action", "Thinking in Java", "Sun's Java Tutorials"};

        for(String title : availableBooksTitles){
            Book newBook = new Book(title);
            availableBooks.add(newBook);
        }

        fillSubmenuMaps();
        fillAvailableMainMethodsMap();
    }

    public Map getSubmenuMapInfo(String submenuName) {
        return submenuMasterMap.get(submenuName);
    }

    public String welcome() {
        return "WELCOME TO THE BIBLIOTECA APP";
    }

    public void fillAvailableMainMethodsMap(){
        methodMap.put("List Books", new BibliotecaCommand() {
            public Map runCommand() { return listBooks(); };
        });
        methodMap.put("Return Book", new BibliotecaCommand() {
            public Map runCommand() { return returnBookMenu(); };
        });
    }

    public void fillSubmenuMaps() {
        fillHeaderMap();
        fillPromptMap();
        fillInvalidMap();
    }

//MENU METHODS
    public String[] listMainMenu() {
        return availableMenuOptions;
    }

    public Map chooseMenuOption(int menuNumber) {
        int index = menuNumber - 1;
        Map menuOptions = new HashMap();

        if(index >= availableMenuOptions.length || index < 0) {
            String[] submenuName = {"Invalid"};
            String[] submenuData = {"Select a valid option!"};
            menuOptions = fillReturnMap(menuOptions, submenuName, submenuData);
        } else {
            menuOptions = methodMap.get(availableMenuOptions[index]).runCommand();
        }

        return menuOptions;
    }

//RENT BOOK METHODS
    public Map listBooks() {
        int numberOfBooks = availableBooks.size();
        String[] bookTitles = new String[numberOfBooks];
        Map<String, String[]> listBooksReturnValue = new HashMap<String, String[]>();
        String[] submenuName = {"List Books"};

        for(int i=0; i < availableBooks.size(); i++){
            bookTitles[i] = availableBooks.get(i).getTitle()+" ["+availableBooks.get(i).bookStatus()+"]";
        }

        return fillReturnMap(listBooksReturnValue, submenuName, bookTitles);
    }

    public String checkoutBook(int bookNumber) {
        int index = bookNumber - 1;
        String returnValue = "That book is not available.";

        if(hasArrayIndex(index, availableBooks.size())) {
            if(!availableBooks.get(index).isRented()){
                Book rentedBook = availableBooks.get(index);
                rentedBook.rentBook();
                returnValue = "Thank you! Enjoy the book '"+rentedBook.getTitle()+"'";
            }
        }

        updateRentedBooksArray();

        return returnValue;
    }

//RETURN BOOK METHODS
    public Map returnBookMenu() {
        String[] rentedBooksArray = {};
        Map<String, String[]> rentedBooksReturnValue = new HashMap<String, String[]>();
        String[] submenuName = {"Return Book"};

        updateRentedBooksArray();

        if (rentedBooks.size() != 0) {
            rentedBooksArray = new String[rentedBooks.size()];

            for(int i=0; i < rentedBooks.size(); i++){
                rentedBooksArray[i] = rentedBooks.get(i).getTitle();
            }
        }

        return fillReturnMap(rentedBooksReturnValue, submenuName, rentedBooksArray);
    }

    public String returnBook(int bookNumber) {
        int index = bookNumber - 1;
        String returnValue = "That is not a valid book to return.";

        if(!rentedBooks.isEmpty() && hasArrayIndex(index, rentedBooks.size())) {
            if(rentedBooks.get(index).isRented()){
                Book rentedBook = rentedBooks.get(index);
                rentedBook.returnBook();
                returnValue = "Thank you for returning the book '"+rentedBook.getTitle()+"'";
            }
        }

        return returnValue;
    }

//HELPER METHODS
//MAP INSTANTIATION RELATED METHODS
    private void fillHeaderMap() {
        Map<String, String> submenuHeaderMap = new HashMap<String, String>();

        submenuHeaderMap.put("List Books", "LIST BOOKS SUBMENU - CHOSE [NUMBER] TO RENT BOOK");
        submenuHeaderMap.put("Return Book", "RETURN BOOK SUBMENU - CHOSE [NUMBER] TO RETURN BOOK");
        submenuMasterMap.put("Header", submenuHeaderMap);
    }

    private void fillPromptMap() {
        Map<String, String> submenuPromptMap = new HashMap<String, String>();

        submenuPromptMap.put("List Books", "Rent book number: ");
        submenuPromptMap.put("Return Book", "Return book number: ");
        submenuMasterMap.put("Prompt", submenuPromptMap);
    }

    private void fillInvalidMap() {
        Map<String, String> submenuInvalidAnswerMap = new HashMap<String, String>();

        submenuInvalidAnswerMap.put("List Books", "That book is not available.");
        submenuInvalidAnswerMap.put("Return Book", "That is not a valid book to return.");
        submenuMasterMap.put("Invalid", submenuInvalidAnswerMap);
    }

    private Map fillReturnMap(Map mapToBeFilled, String[] submenuName, String[] data) {
        mapToBeFilled.put("Submenu", submenuName);
        mapToBeFilled.put("Data", data);

        return mapToBeFilled;
    }

//OTHER HELPER METHODS
    private void updateRentedBooksArray(){
        rentedBooks = new ArrayList<Book>();

        for(Book book : availableBooks) {
            if(book.isRented()) {
                rentedBooks.add(book);
            }
        }
    }

    private Boolean hasArrayIndex(int index, int length){
        Boolean answer = false;

        if (index < length && index >= 0){
            answer = true;
        }

        return answer;
    }
}